#ifndef SOLVER_H
#define SOLVER_H

#include <climits>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <map>
#include <set>
#include <omp.h>

#include <iostream>
#include <fstream>
#include <filesystem>
namespace fs = std::filesystem;

#include "cblas.h"

// print color macro
#define DEF "\033[0m"
#define RED "\033[1;;31m"
#define GRE "\033[1;;32m"
#define YEL "\033[1;;33m"

#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif

#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif

#ifndef ABS
#define ABS(x) ((x) >= 0 ? (x) : -(x))
#endif

#define ABORT(msg)                                                                                                     \
    {                                                                                                                  \
        fprintf(stderr, RED "%s\n" RED, msg);                                                                          \
        exit(1);                                                                                                       \
    }

typedef enum
{
    NONE,
    CSR,
    DENSE,
    CSC
} TYPE;

typedef struct
{
    int m, n, nonz;
    double *a;
} TILE;

typedef struct
{
    int m, n, nonz;
    TILE **tile;
} BLOCK;

typedef struct
{
    TYPE type;

    char *name;
    double *a;
    int *row, *col;
    int real_m, real_n, real_nonz; // real size
    int symmetric;

    // construct
    int block_size;
    int tile_size;

    // preprocess
    double threshold;
    int *P, *Q;

    // block
    int m, n, nonz;
    BLOCK **block;

    // bandwidth, control number of threads
    int bandwidth;

    // Information
    double time;
    double time_U; // U
    double time_L; // L
    double time_C; // Schur Complement

    double reorder_time;
    double fact_time; // factorization time
    double solve_time;

    int num_tgemm;
    double mem; // 内存占用(MB)

    int num_block;
    int num_tile;
    double tile_per_block;

    int num_csr;
    int num_dense;
    double nonz_per_A;
    double nonz_per_csr;
    double nonz_per_dense;


    double error;
    char* output_path;
} MATRIX;

// 划分public和private函数

// constructor, destructor and init
TILE *construct_T(int m, int n);
BLOCK *construct_B(int m, int n);
MATRIX *construct_M(void);
void destroy_T(TILE *T);
void destroy_B(BLOCK *B);
void destroy_M(MATRIX *A);
void read_to_csr(const char *filename, MATRIX *A);
void readrb(const char *file, MATRIX *A);
void readcsc(const char *file, MATRIX *A);


void init(MATRIX *A, int block_size, int tile_size);

// bandwidth compress
void bandwidth_compress_boost(MATRIX *A);

// pivot
void pivot(MATRIX *A, int bk, int tk, int k);

// factorization
void dgetrf(MATRIX *A);

// solve
void solve(MATRIX *A, double *b);
void gemv(MATRIX *A, const double *x, double *y);

// utils
void set_size(MATRIX *A, int block_size, int tile_size);
double set_time(struct timeval *val);
double get_time(struct timeval *val);
void permA(MATRIX *A, int *perm);
int get_bs(const MATRIX *A, int b);
int get_ts(const MATRIX *A, int b, int t);
void get_position(const MATRIX *A, int i, int *b, int *t, int *r);
void print_row(MATRIX *A, int bi, int ti, int ai);
void print_col(MATRIX *A, int bj, int tj, int aj);
void print_TILE(TILE *T);
void print_BLOCK(BLOCK *B);
void print_MATRIX(MATRIX *A);
void pattern(const MATRIX *A);
void pattern2(const MATRIX* A);

int rcm_with_degree(MATRIX *A, int *perm);
int get_bw(MATRIX *A, int n);
int scatter(const MATRIX *A);

void information(MATRIX *A);
void progress_bar(const char *name, int total, int n);

void dense_to_csr(TILE *T, double threshold);
void csr_to_dense(TILE *T);
void update_nonz(TILE *T);
void check_blank(TILE **T);

// memory
void *my_malloc(int n, int size);
void my_free(void *p, int n, int size);
double get_memory();

// get perm
int get_perm(int m, int n, int nonz, int *row, int *col, int *perm);
void get_mc64(MATRIX* A);
void get_rcm(MATRIX* A);
void get_prcm(MATRIX* A);
// reference
void dreadrb(const char *file, int *nrow, int *ncol, int *nonz, double **A_csr, int **A_col, int **A_row);

#endif
